﻿// NFakeMail - A fake SMTP Server for software testing.
// 
// Copyright (C) 2010, João Angelo
// 
// This file is part of NFakeMail.
//
// NFakeMail is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// NFakeMail is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with NFakeMail; if not, see <http://www.gnu.org/licenses/>.

namespace NFakeMail
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;

    /// <summary>
    /// Provides helper methods to deal with MIME encoded messages.
    /// </summary>
    /// <author> jmangelo </author>
    /// <history>
    /// ______________________________________________________________________________________
    /// History :
    /// 20100816 [+] jmangelo     Initial version
    /// ______________________________________________________________________________________
    /// </history>
    internal static class MimeUtils
    {
        public static string DecodeMimeEncodedWords(string text)
        {
            const string EncodedWordRegexPattern = @"=\?(?<charset>[-\w]+)\?(?<encoding>[qQbB])\?(?<encodedtext>.*?)\?=";

            var encodedWords = Regex.Matches(text, EncodedWordRegexPattern);

            if (encodedWords.Count == 0)
            {
                return text;
            }

            bool hasQuotedPrintableEncoding = encodedWords.Cast<Match>().Any(
                m => m.Groups["encoding"].Value == "q" || m.Groups["encoding"].Value == "Q");

            if (hasQuotedPrintableEncoding)
            {
                //// TODO : P2 : Add support for quoted printable decoding

                throw new NotSupportedException(
                    "The Q encoding used in the MIME encoded word is not supported.");
            }

            var subject = new StringBuilder();

            var supportedCharSets = new HashSet<string>(
                Encoding.GetEncodings().Select(e => e.Name),
                StringComparer.OrdinalIgnoreCase);

            Encoding encoding = null;
            var encodedData = new MemoryStream();
            foreach (Match encodedWord in encodedWords)
            {
                string charSet = encodedWord.Groups["charset"].Value;

                if (!supportedCharSets.Contains(charSet))
                {
                    string message = String.Format(
                        CultureInfo.InvariantCulture,
                        "The character set ({0}) used in the MIME encoded word is not supported.",
                        charSet);

                    throw new NotSupportedException(message);
                }

                string encodedText = encodedWord.Groups["encodedtext"].Value;

                var wordEncoding = Encoding.GetEncoding(charSet);

                if (encoding == null)
                {
                    encoding = wordEncoding;
                }

                if (encoding != wordEncoding)
                {
                    throw new NotSupportedException(
                        "Encoded words in more than one character set are not supported.");
                }

                byte[] bytes = Convert.FromBase64String(encodedText);
                encodedData.Write(bytes, 0, bytes.Length);
            }

            subject.Append(encoding.GetString(encodedData.ToArray()));

            return subject.ToString();
        }

        public static string GetHeadersSection(string mimeMessage)
        {
            // MIME headers end at the first blank line
            int mimeHeadersEndIndex = Regex.Match(mimeMessage, "\n\n").Index;

            string mimeHeadersSection = mimeMessage.Substring(0, mimeHeadersEndIndex);

            return mimeHeadersSection;
        }

        public static string GetHeaderValue(string header, string mimeHeaders)
        {
            using (var reader = new StringReader(mimeHeaders))
            {
                string line = null;

                do
                {
                    line = reader.ReadLine();
                }
                while (line != null && !line.StartsWith(header, StringComparison.OrdinalIgnoreCase));

                if (line == null)
                {
                    return string.Empty;
                }

                string value = line;

                // Handle multi-line headers
                line = reader.ReadLine();

                while (line != null && (line.StartsWith("\t") || line.StartsWith(" ")))
                {
                    value += line;
                    line = reader.ReadLine();
                }

                var parts = value.Split(new char[] { ':' }, 2);

                if (parts.Length != 2)
                {
                    return string.Empty;
                }

                return parts[1].TrimStart();
            }
        }
    }
}
